﻿using ArtMath.Attribute;
using ArtMath.Component.Line;
using ArtMath.Component.Point;
using ArtMath.Converter;
using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using ArtMath.Flag;
using ArtMath.PointCollection;
using ArtMath.Resource;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using ArtMath.CommonType;

namespace ArtMath.Component.Polygon
{
    [Serializable]
    public abstract class PolygonBase : ClosedShapeBase
    {
        List<LineSegmentBase> lines = new List<LineSegmentBase>();
        //List<PointD[]> track = new List<PointD[]>();
        PropertyDisplayer zxDisplayer;
        public PolygonBase(SketchPad pad, string name, ToolTypeName toolType, bool addToList = true) : base(pad, name, toolType, addToList)
        {
            BaryCenterDisplayer = new PropertyDisplayer(this, "BaryCenter");
        }
        PointCollectionsManager buildPCM(Func<LineSegmentBase, PointCollectionsManager> func)
        {
            if (func == null) return null;
            PointCollectionsManager res = new PointCollectionsManager(), last = null, first = null;
            foreach (LineSegmentBase item in Lines)
            {
                PointCollectionsManager current = func(item);
                res.AddPointCollection(current);
                if (last != null)
                {
                    foreach (PointCollectionBase end in last.PointCollections)
                    {
                        foreach (PointCollectionBase start in current.PointCollections)
                        {
                            res.ConnectHeadAndTail(end, start);
                        }
                    }
                }
                last = current;
                if (first == null)
                {
                    first = current;
                }
            }
            foreach (PointCollectionBase end in last.PointCollections)
            {
                foreach (PointCollectionBase start in first.PointCollections)
                {
                    res.ConnectHeadAndTail(end, start);
                }
            }
            return res;
        }
        public override PointCollectionsManager Skeleton
        {
            get
            {
                return buildPCM((LineSegmentBase line) => { return line.Skeleton; });
            }
        }
        public override PointCollectionsManager PSkeleton
        {
            get
            {
                return buildPCM((LineSegmentBase line) => { return line.PSkeleton; });
            }
        }
        public override PointCollectionsManager NewSkeleton
        {
            get
            {
                return buildPCM((LineSegmentBase line) => { return line.NewSkeleton; });
            }
        }
        public override PointCollectionsManager NewPSkeleton
        {
            get
            {
                return buildPCM((LineSegmentBase line) => { return line.NewPSkeleton; });
            }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("EdgesNumber")]
        public virtual int LinesCount
        {
            get { return Lines.Count; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "BaryCenter", "Displayer")]
        public PropertyDisplayer BaryCenterDisplayer
        {
            get { return zxDisplayer; }
            set { zxDisplayer = value; }
        }
        /// <summary>
        /// 重心
        /// </summary>
        [MultilingualCategory("Parameter"), MultilingualDisplayName("BaryCenter"), TypeConverter(typeof(IntelliPointConverter))]
        public PointD BaryCenter { get; protected set; }
        [Browsable(false)]
        public PointD NewBaryCenter
        {
            get
            {
                PointD[] tmp = new PointD[Lines.Count];
                for (int i = 0; i < tmp.Length; i++)
                {
                    tmp[i] = InPoints[i].NewXY;
                }
                return Utility.GetBaryCenter(tmp);
            }
        }
        [Browsable(false)]
        public PointD PBaryCenter { get; protected set; }
        [Browsable(false)]
        public PointD NewPBaryCenter
        {
            get { return Pad.XYToPxy(NewBaryCenter); }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("EdgeCollection")]
        public virtual List<LineSegmentBase> Lines
        {
            get { return lines; }
        }
        public override PointD MassPoint
        {
            get { return BaryCenter; }
        }
        public override bool IsEntity
        {
            get
            {
                return false;
            }
        }
        public override void ComponentBuilding(Base component)
        {
            PBaryCenter = Pad.XYToPxy(BaryCenter);
            AppendStatus($"{Resources.CommaSeparate}{string.Format(Resources.Translate("Of"), Resources.Translate("BaryCenter"), Resources.Translate("Coordinate"))}{Resources.ColonSeparate}{Pad.ShowPoint(BaryCenter)}");
            base.ComponentBuilding(component);
        }
        public void SetLine(int index, LineSegmentBase value)
        {
            if (index < lines.Count)
            {
                lines[index]?.InHosts.Remove(this);
                lines[index] = value;
                lines[index]?.InHosts.Add(this);
            }
        }
        //public override void AddTracks()
        //{
        //    PointD[] pts = new PointD[lines.Count];
        //    for (int i = 0; i < pts.Length; i++)
        //    {
        //        pts[i] = lines[i].Point1.Pxy;
        //    }
        //    track.Add(pts);
        //}
        public override void InChildrenIterator(Predicate<ShapeBase> act)
        {
            if (act == null) return;
            foreach (LineSegmentBase item in Lines)
            {
                if (item != null)
                {
                    if (!act(item)) break;
                }
            }
        }
        //public override void ClearTracks()
        //{
        //    track.Clear();
        //    base.ClearTracks();
        //}
        public override void RefreshPropertyDisplayersLocation()
        {
            BaryCenterDisplayer.RefreshLocation();
            base.RefreshPropertyDisplayersLocation();
        }
        public override void RefreshPropertyDisplayersText()
        {
            BaryCenterDisplayer.RefreshText();
            base.RefreshPropertyDisplayersText();
        }
        public override void PaintProperties(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible)
            {
                BaryCenterDisplayer.Paint(gra);
            }
            base.PaintProperties(gra);
        }
        public override void Paint(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible)
            {
                bool fill = true;
                PointF[] pts = new PointF[lines.Count];
                for (int i = 0; i < pts.Length; i++)
                {
                    if (lines[i]?.Point1 != null)
                    {
                        pts[i] = lines[i].Point1.Pxy.ToPointF();
                    }
                    else
                    {
                        fill = false;
                        break;
                    }
                }
                Brush bru;
                if (fill && (bru = FillBrush) != null) gra.FillPolygon(bru, pts);
            }
            base.Paint(gra);
        }
        [Browsable(false), MultilingualDisplayName("Formula")]
        public override string Formula
        {
            get
            {
                return base.Formula;
            }
        }
        [Browsable(false)]
        public override PropertyDisplayer FormulaDisplayer
        {
            get
            {
                return base.FormulaDisplayer;
            }

            set
            {
                base.FormulaDisplayer = value;
            }
        }
        public override bool IsConnectedLineGroup
        {
            get
            {
                return true;
            }
        }
        public override void ForEachStraightLine(Predicate<StraightLineBase> act)
        {
            if (act == null) return;
            foreach (LineSegmentBase item in Lines)
            {
                if (!act(item))
                {
                    break;
                }
            }
        }
        public override PointCollectionBase[] GetInPointMovingRange(PointBase movePoint, Base sender = null)
        {
            LineSegmentBase ls = sender as LineSegmentBase;
            if (ls != null)
            {
                int index = Lines.IndexOf(ls);
                if (index >= 0 && Lines[index].Movable)
                {
                    int movcnt = CountMovablePoints();
                    if (movcnt == InPoints.Length - 1)
                    {
                        return new PointCollectionBase[] { new StraightLine(fixedPoints[0].NewXY, fixedPoints[0].NewXY.GetSlope(movePoint.NewXY)) };
                    }
                    else if (movcnt == 2)
                    {
                        int last = index == 0 ? Lines.Count - 1 : index - 1, next = index == Lines.Count - 1 ? 0 : index + 1;
                        if (movePoint == Lines[index].Point1)
                        {
                            return new PointCollectionBase[] { new StraightLine(Lines[last].Point1.NewXY, Lines[last].NewSlope) };
                        }
                        else if (movePoint == Lines[index].Point2)
                        {
                            return new PointCollectionBase[] { new StraightLine(Lines[next].Point2.NewXY, Lines[next].NewSlope) };
                        }
                    }
                }
            }
            return base.GetInPointMovingRange(movePoint, sender);
        }
        public override void InternalPointMove(PointBase movePoint, Base sender = null)
        {
            int index = Lines.IndexOf(sender as LineSegmentBase);
            if (index >= 0 && Lines[index].Point1.Movable && Lines[index].Point2.Movable)
            {
                int movcnt = CountMovablePoints();
                if (movcnt == 2)
                {
                    int last = index == 0 ? Lines.Count - 1 : index - 1, next = index == Lines.Count - 1 ? 0 : index + 1;
                    if (movePoint == Lines[index].Point1)
                    {
                        Lines[index].Point2.Move(Utility.GetCrossPoint(movePoint.NewPxy, Lines[index].NewPSlope, Lines[next].Point2.NewPxy, Lines[next].NewPSlope));
                    }
                    else if (movePoint == Lines[index].Point2)
                    {
                        Lines[index].Point1.Move(Utility.GetCrossPoint(movePoint.NewPxy, Lines[index].NewPSlope, Lines[last].Point1.NewPxy, Lines[last].NewPSlope));
                    }
                    return;
                }
            }
            base.InternalPointMove(movePoint, sender);
        }
        public override void ComponentChanged(ShapeBase component)
        {
            PointD[] pts = new PointD[Lines.Count], pts2 = new PointD[Lines.Count];
            Circumference = 0;
            for (int i = 0; i < pts.Length; i++)
            {
                pts[i] = InPoints[i].NewPxy;
                pts2[i] = InPoints[i].NewXY;
                Circumference += Lines[i].NewLength;
            }
            Area = 0;
            for (int i = 0; i < pts2.Length; i++)
            {
                int iNext = i == pts2.Length - 1 ? 0 : i + 1,
                    iLast = i == 0 ? pts2.Length - 1 : i - 1;
                Area += pts2[i].Y * (pts2[iLast].X - pts2[iNext].X);
            }
            Area = Math.Abs(Area / 2);
            FillRegion = Utility.GetSmallestSurroundRect(pts);
            BaryCenter = Utility.GetBaryCenter(pts2);
            PBaryCenter = Pad.XYToPxy(BaryCenter);
            base.ComponentChanged(component);
        }
        public override void CreateComponents()
        {
            if (InPoints == null) InPoints = new PointBase[Lines.Count];
            PointD[] pts = new PointD[Lines.Count];
            for (int i = 0; i < pts.Length; i++)
            {
                InPoints[i] = Lines[i].Point1;//多边形不是顶点的直系父元件，因此不能用SetPoint方法
                pts[i] = InPoints[i].Pxy;
            }
            FillRegion = Utility.GetSmallestSurroundRect(pts);
            base.CreateComponents();
        }
        public override PropertyDisplayer[] PropertyDisplayersCheckNearBy(PointD Plocation)
        {
            List<PropertyDisplayer> res = new List<PropertyDisplayer>(base.PropertyDisplayersCheckNearBy(Plocation));
            if (BaryCenterDisplayer.InRegion(Plocation))
            {
                res.Add(BaryCenterDisplayer);
            }
            return res.ToArray();
        }
    }
}
